{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tricky Stuff\n",
    "\n",
    "This file highlights some tricky aspects of Julia (from the perspective of a Matlab user)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "printyellow (generic function with 1 method)"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "using Dates, LinearAlgebra\n",
    "\n",
    "include(\"printmat.jl\")   #function for prettier matrix printing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# An Nx1 Array is not a Vector\n",
    "\n",
    "and it sometimes matters. \n",
    "\n",
    "Julia has both vectors and Nx1 arrays (the latter being a special case of NxM arrays). They can often be used interchangeably, but not always (see below for an example).\n",
    "\n",
    "In particular, you typically use a vector when you want to pull out particular rows from a larger array."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "v and v2 look similar, but they have different sizes: \n",
      "         1\n",
      "         1\n",
      "\n",
      "         1\n",
      "         1\n",
      "\n",
      "size of v and v2: (2,) (2, 1)\n",
      "\n",
      "x: \n",
      "        11        12\n",
      "        21        22\n",
      "        31        32\n",
      "\n",
      "x[v,:] is\n",
      "        11        12\n",
      "        11        12\n",
      "\n"
     ]
    }
   ],
   "source": [
    "v  = ones(Int,2)                     #a vector with two elements\n",
    "v2 = ones(Int,2,1)                   #a 2x1 matrix (Array)\n",
    "\n",
    "println(\"v and v2 look similar, but they have different sizes: \")\n",
    "printmat(v)\n",
    "printmat(v2)\n",
    "println(\"size of v and v2: \",size(v),\" \",size(v2))\n",
    "\n",
    "x = [11 12;21 22;31 32]\n",
    "println(\"\\nx: \")\n",
    "printmat(x)\n",
    "\n",
    "println(\"x[v,:] is\")\n",
    "printmat(x[v,:])                    #instead, x[v2,:] gives an 2x1x2 array"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Array .+ scalar Requires the dot (.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "         2\n",
      "         3\n",
      "\n"
     ]
    }
   ],
   "source": [
    "y = [1;2] .+ 1              #do not forget the dot\n",
    "printmat(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Creating Variables in a Loop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.28366218546322625\n",
      "Oden: 0.28366218546322625\n"
     ]
    }
   ],
   "source": [
    "for i = 1:5\n",
    "    global Tor         #without this, Tor is not seen outside the loop  \n",
    "    Tor = cos(i)\n",
    "end\n",
    "println(Tor)\n",
    "\n",
    "\n",
    "Oden = Inf              \n",
    "for i = 1:5\n",
    "    #global Oden        #only needed in REPL/scripts\n",
    "    Oden = cos(i)       #will overwrite an existing value \n",
    "end\n",
    "println(\"Oden: \",Oden)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Threads.@threads and Variable Scope\n",
    "\n",
    "Code like this\n",
    "```\n",
    "v = 1:2\n",
    "Threads.@threads for i = 1:N\n",
    "    v = something \n",
    "    x = SomeFunction(v)\n",
    "end\n",
    "```\n",
    "can create unexpected results since the threads are sharing `v`. This is solved by declaring `v` inside the loop to be `local`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "this should always be zero. Run a few times.\n",
      "[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 15.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 24.0, 0.0, 0.0, 0.0, 0.0, 18.0, 0.0, 0.0, 0.0, 0.0, 11.0, 0.0, 27.0, 0.0, 16.0, 2.0, 0.0, 0.0, 0.0, 2.0, 24.0, 29.0, 0.0, 19.0, 38.0, 0.0, 0.0, 0.0, 28.0, 32.0, 28.0, 30.0, 0.0, 47.0, 0.0, 0.0, 17.0, 0.0, 43.0, 40.0, 0.0, 48.0, 0.0, 2.0, 0.0, 0.0, 50.0, 0.0, 0.0, 0.0, 40.0, 45.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 59.0, 47.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 28.0, 16.0, 0.0, 0.0, 0.0, 63.0, 0.0, 92.0]\n"
     ]
    }
   ],
   "source": [
    "function f2(N)\n",
    "  v = falses(N+1)\n",
    "  x = zeros(Int,N,N)\n",
    "  Threads.@threads for i = 1:N\n",
    "    #local v                   #comment out to see the problem    \n",
    "    v    = falses(N)\n",
    "    v[i] = true\n",
    "    x[v,i] .= i               \n",
    "  end\n",
    "  return x\n",
    "end\n",
    "\n",
    "println(\"this should always be zero. Run a few times.\")\n",
    "M = 100\n",
    "dev = zeros(M)\n",
    "for i = 1:M\n",
    "  dev[i] = maximum(abs,f2(i) - diagm(1:i))\n",
    "end\n",
    "println(dev)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# A Heterogeneous Array"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To create a 'cell array' (a heterogeneous Array), use `[x1,x2,...]`\n",
    "\n",
    "Alternatively, you can preallocate as in `B = Array{Any}(undef,3)` and then fill by, for instance, `B[1] = [11 12]`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "The array A: \n",
      "        11        12\n",
      "        21        22\n",
      "\n",
      "A nice dog\n",
      "\n",
      "        27\n",
      "\n",
      "\n",
      "The array B: \n",
      "        11        12\n",
      "        21        22\n",
      "\n",
      "A nice dog\n",
      "\n",
      "        27\n",
      "\n"
     ]
    }
   ],
   "source": [
    "A = [[11 12;21 22],\"A nice dog\",27]\n",
    "\n",
    "println(\"\\nThe array A: \")\n",
    "foreach(i->printmat(A[i]),1:length(A))     #print each element of A\n",
    "\n",
    "B = Array{Any}(undef,3)\n",
    "B[1] = [11 12]\n",
    "B[2] = \"A bad cat\"\n",
    "B[3] = pi\n",
    "\n",
    "println(\"\\nThe array B: \")\n",
    "foreach(i->printmat(A[i]),1:length(A))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# An Array of Arrays\n",
    "\n",
    "can be initialized by comprehension (see below). (Do *not* use fill. See \"A Reshaped Array\" for why.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x[1]\n",
      "   -99.000     0.000\n",
      "     0.000     0.000\n",
      "\n",
      "x[2]\n",
      "     0.000     0.000\n",
      "     0.000     0.000\n",
      "\n"
     ]
    }
   ],
   "source": [
    "x = [zeros(2,2) for i=1:2]        #a vector of two matrices\n",
    "x[1][1,1] = -99\n",
    "\n",
    "println(\"x[1]\")\n",
    "printmat(x[1])\n",
    "\n",
    "println(\"x[2]\")\n",
    "printmat(x[2])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Arrays are Different...\n",
    "\n",
    "Vectors and matrices (arrays) can take lots of memory space, so **Julia is designed to avoid unnecessary copies of arrays**."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Issue 1. B = A Creates Two Names of the Same Array\n",
    "\n",
    "If A is an array, then\n",
    "```\n",
    "B = A\n",
    "```\n",
    "creates two names of the *same* matrix. If you later change A, then B is changed automatically. (Similarly, if you change B, then A is changed automatically.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "old A,B,C (each is a column): \n",
      "         1         1         1\n",
      "         2         2         2\n",
      "\n",
      "after changing element A[2] to -999, A,B,C are:\n",
      "         1         1         1\n",
      "      -999      -999         2\n",
      "\n",
      "\n",
      "\u001b[34m\u001b[1mNotice that B changed, but C did not\u001b[22m\u001b[39m\n"
     ]
    }
   ],
   "source": [
    "A = [1,2]\n",
    "B = A                                 #A and B are the same\n",
    "C = A .+ 0                            #A and C are not the same\n",
    "println(\"old A,B,C (each is a column): \")\n",
    "printmat([A B C])\n",
    "\n",
    "A[2] = -999\n",
    "println(\"after changing element A[2] to -999, A,B,C are:\")\n",
    "printmat([A B C])\n",
    "\n",
    "printblue(\"\\nNotice that B changed, but C did not\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Issue 2. A Reshaped Array still Refers to the Original Array\n",
    "\n",
    "If you create a reshaped array by either \n",
    "```\n",
    "B = reshape(A,n,m)\n",
    "C = vec(A)\n",
    "D = A'\n",
    "E = fill(A,2)\n",
    "```\n",
    "then A, B, C, D and E contain the same values. Changing one changes the others automatically."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "original A: \n",
      "         1         2\n",
      "\n",
      "old B, C and D (each is a column): \n",
      "         1         1         1\n",
      "         2         2         2\n",
      "\n",
      "B, C and D after changing element A[2] to -999\n",
      "         1         1         1\n",
      "      -999      -999      -999\n",
      "\n",
      "E[1] and E[2] after changing element A[2] to -999\n",
      "         1      -999\n",
      "\n",
      "         1      -999\n",
      "\n",
      "\n",
      "\u001b[34m\u001b[1mNotice that B, C, D and E also changed\u001b[22m\u001b[39m\n"
     ]
    }
   ],
   "source": [
    "A = [1 2]\n",
    "println(\"original A: \")\n",
    "printmat(A)\n",
    "\n",
    "B = reshape(A,2,1)\n",
    "C = vec(A)\n",
    "D = A'\n",
    "E = fill(A,2)\n",
    "\n",
    "println(\"old B, C and D (each is a column): \")\n",
    "printmat([B C D])\n",
    "\n",
    "A[2] = -999\n",
    "println(\"B, C and D after changing element A[2] to -999\")\n",
    "printmat([B C D])\n",
    "println(\"E[1] and E[2] after changing element A[2] to -999\")\n",
    "printmat(E[1])\n",
    "printmat(E[2])\n",
    "\n",
    "printblue(\"\\nNotice that B, C, D and E also changed\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Issue 3. Changing an Array Inside a Function Can Have Effects *Outside* the Function"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When you use an array as a function argument, then that is passed as a reference to the function.\n",
    "\n",
    "This means that if you change some elements of the array (`A[1] = A[1]/2`, say) inside the function, then it will also affect the array outside the function (even if they have different names).\n",
    "\n",
    "In contrast, if you change the entire array (`A/2`, say) inside the function, then that does not affect the array outside the function.\n",
    "\n",
    "This applies to arrays, but not to scalars or strings.\n",
    "\n",
    "If you really need an independent copy of an array, create it by\n",
    "\n",
    "`B = copy(A)`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "original x:      1.000     2.000\n",
      "x (outside function) after calling f1(x):      0.500     2.000\n",
      "\n",
      "original x:      1.000     2.000\n",
      "x (outside function) after calling f2(x):      1.000     2.000\n",
      "\n",
      "\u001b[34m\u001b[1mNotice that f1() changed x also outside the function, but f2() did not\u001b[22m\u001b[39m\n"
     ]
    }
   ],
   "source": [
    "function f1(A)\n",
    "    A[1] = A[1]/2          #changes ELEMENTS of A, affects outside value\n",
    "  return A\n",
    "end\n",
    "function f2(A)\n",
    "    A = A/2                #changes all of A, does not affect outside value\n",
    "  return A\n",
    "end\n",
    "\n",
    "x  = [1.0 2.0]\n",
    "printlnPs(\"original x: \",x)\n",
    "\n",
    "y1 = f1(x)\n",
    "printlnPs(\"x (outside function) after calling f1(x): \",x)\n",
    "\n",
    "x  = [1.0 2.0]\n",
    "printlnPs(\"\\noriginal x: \",x)\n",
    "\n",
    "y2 = f2(x)\n",
    "printlnPs(\"x (outside function) after calling f2(x): \",x)\n",
    "\n",
    "printblue(\"\\nNotice that f1() changed x also outside the function, but f2() did not\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Issue 4. Arrays in Heterogeneous Arrays Still Refer to the Underlying Arrays\n",
    "\n",
    "An array `a` inside another array `A`is really just a referece to the existing `a`. Changing elements of `a` will change `A`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A:\n",
      "        11        12\n",
      "        21        22\n",
      "\n",
      "A nice dog\n",
      "\n",
      "        27\n",
      "\n",
      "\n",
      "\u001b[34m\u001b[1mChange a[1,1] to -999 and notice that also A changes\u001b[22m\u001b[39m\n",
      "A:\n",
      "      -999        12\n",
      "        21        22\n",
      "\n",
      "A nice dog\n",
      "\n",
      "        27\n",
      "\n",
      "\n",
      "\u001b[34m\u001b[1mChange a[1,1] to 123 and notice that also A changes\u001b[22m\u001b[39m\n",
      "       123        12\n",
      "        21        22\n",
      "\n",
      "       123        12\n",
      "        21        22\n",
      "\n",
      "A nice dog\n",
      "\n",
      "        27\n",
      "\n"
     ]
    }
   ],
   "source": [
    "a = [11 12;21 22]\n",
    "A = [a,\"A nice dog\",27]               #a heterogeneous array\n",
    "println(\"A:\")\n",
    "foreach(i->printmat(A[i]),1:length(A))\n",
    "\n",
    "printblue(\"\\nChange a[1,1] to -999 and notice that also A changes\")\n",
    "a[1,1] = -999\n",
    "println(\"A:\")\n",
    "foreach(i->printmat(A[i]),1:length(A))\n",
    "\n",
    "B = pushfirst!(A,a)\n",
    "printblue(\"\\nChange a[1,1] to 123 and notice that also A changes\")\n",
    "a[1,1] = 123\n",
    "foreach(i->printmat(A[i]),1:length(A))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "@webio": {
   "lastCommId": null,
   "lastKernelId": null
  },
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Julia 1.3.0",
   "language": "julia",
   "name": "julia-1.3"
  },
  "language_info": {
   "file_extension": ".jl",
   "mimetype": "application/julia",
   "name": "julia",
   "version": "1.3.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
